package group.sshd.redis.client.terminal.ui;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.gui2.*;
import com.googlecode.lanterna.gui2.dialogs.ListSelectDialog;
import com.googlecode.lanterna.gui2.dialogs.MessageDialog;
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton;
import com.googlecode.lanterna.gui2.dialogs.TextInputDialog;
import com.googlecode.lanterna.input.KeyStroke;
import com.googlecode.lanterna.input.KeyType;
import com.googlecode.lanterna.screen.Screen;
import group.sshd.redis.client.terminal.config.RedisConfig;
import group.sshd.redis.client.terminal.config.RedissonFactory;
import org.redisson.api.RBucket;
import org.redisson.api.RFuture;
import org.redisson.api.RScript;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author bolin
 * @create 2020-11
 * @desc
 */
@Component
public class TerminalUI implements Runnable {

    @Autowired
    private RedisConfig redisConfig;

    @Autowired
    private RedissonFactory redissonFactory;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private WindowBasedTextGUI gui;

    @Autowired
    private Screen screen;

    @Override
    public void run() {
        while (true) {
            String[] names = redisConfig.getNodeNames();
            String name = ListSelectDialog.showDialog(gui, "选择连接", "", names);
            if (name != null) {
                try {
                    screen.clear();
                    screen.refresh();
                    show(name);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                break;
            }
        }
    }

    public void show(String name) {
        RedissonClient redissonClient = redissonFactory.connRedis(name);
        try {
            AtomicBoolean canWrite = new AtomicBoolean(false);
            TextBox show = new TextBox(new TerminalSize(80, 20), TextBox.Style.MULTI_LINE);
            TextBox TTL = new TextBox(new TerminalSize(80, 1), TextBox.Style.MULTI_LINE);
            AtomicReference<String> key = new AtomicReference<>();
            TextBox keyInput = new TextBox(new TerminalSize(20, 1), TextBox.Style.SINGLE_LINE);
            Button searchKey = new Button(" 查询", () -> {
                key.set(null);
                canWrite.set(false);
                if (keyInput.getText() != null && keyInput.getText().trim().length() > 0) {
                    canWrite.set(true);
                    key.set(keyInput.getText());
                    try {
                        RBucket bucket = redissonClient.getBucket(key.get());
                        show.setText(objectMapper.writeValueAsString(bucket.get()));
                        TTL.setText("" + bucket.remainTimeToLive());
                    } catch (Exception e) {
                        e.printStackTrace();
                        show.setText(e.getMessage());
                    }
                } else {
                    show.setText("输入key进行查询");
                    TTL.setText("");
                }
            });
            // 阻塞
            gui.addWindowAndWait(new BasicWindow("已连接: " + name) {{
                setHints(Arrays.asList(Hint.FULL_SCREEN));
                setComponent(
                        new Panel(new LinearLayout(Direction.VERTICAL))
                                .addComponent(
                                        new Panel(new LinearLayout(Direction.HORIZONTAL))
                                                .setPreferredSize(new TerminalSize(80, 1))
                                                .addComponent(new Label("KEY:"))
                                                .addComponent(keyInput)
                                                .addComponent(searchKey)
                                                .addComponent(new Button(" 写入", () -> {
                                                    if (!canWrite.get()) {
                                                        MessageDialog.showMessageDialog(gui, "提示", "请先查询key");
                                                    } else {
                                                        String input = TextInputDialog.showDialog(gui, "写入redis", "key: " + key.get(), "");
                                                        if (input == null) {
                                                            return;
                                                        } else {
                                                            RBucket bucket = redissonClient.getBucket(key.get());
                                                            bucket.set(input);
                                                            searchKey.handleKeyStroke(new KeyStroke(KeyType.Enter));
                                                        }
                                                    }
                                                }))
                                                .addComponent(new Button(" 删除", () -> {
                                                    if (!canWrite.get()) {
                                                        MessageDialog.showMessageDialog(gui, "提示", "请先查询key");
                                                    } else if (MessageDialogButton.OK == MessageDialog.showMessageDialog(gui, "提示", "确认删除" + key.get(), MessageDialogButton.OK, MessageDialogButton.Cancel)) {
                                                        redissonClient.getBucket(key.get()).set(null);
                                                        searchKey.handleKeyStroke(new KeyStroke(KeyType.Enter));
                                                    }
                                                }))
                                                .addComponent(new Button(" 设置过期时间", () -> {
                                                    if (!canWrite.get()) {
                                                        MessageDialog.showMessageDialog(gui, "提示", "请先查询key");
                                                    } else {
                                                        BigInteger ttl = TextInputDialog.showNumberDialog(gui, "设置过期时间 单位毫秒", "KEY: " + key.get(), "");
                                                        if (ttl != null) {
                                                            redissonClient.getBucket(key.get()).expire(ttl.longValue(), TimeUnit.MILLISECONDS);
                                                            searchKey.handleKeyStroke(new KeyStroke(KeyType.Enter));
                                                        }
                                                    }
                                                }))
                                                .addComponent(new Button(" 切换连接", () -> {
                                                    gui.getWindows().forEach(Window::close);
                                                }))
                                                .withBorder(Borders.singleLine("查询"))
                                )
                                .addComponent(
                                        new Panel().addComponent(TTL.withBorder(Borders.singleLine("TTL -2: KEY不存在 -1: 永不过期 单位毫秒"))).addComponent(show.withBorder(Borders.singleLine("结果")))
                                )
                                .addComponent(
                                        new Panel().addComponent(new Button(" Lua脚本", () -> {
                                                    MessageDialog.showMessageDialog(gui,"警告!","执行时间过长的脚本会导致redis服务器阻塞");
                                                    AtomicReference<String> script = new AtomicReference<>();
                                                    Window scriptWindow = new BasicWindow("Lua脚本");
                                                    scriptWindow.setHints(Arrays.asList(Hint.FULL_SCREEN));
                                                    TextBox textBox = new TextBox(new TerminalSize(80, 20), "", TextBox.Style.MULTI_LINE);
                                                    scriptWindow.setComponent(new Panel()
                                                            .addComponent(textBox.withBorder(Borders.singleLine("执行内容")))
                                                            .addComponent(Panels.horizontal(
                                                                    new Button(" 执行", () -> {
                                                                        script.set(textBox.getText());
                                                                        scriptWindow.close();
                                                                    }),
                                                                    new Button(" 取消",() -> {
                                                                        scriptWindow.close();
                                                                    })
                                                            ).withBorder(Borders.singleLine()))
                                                    );
                                                    gui.addWindowAndWait(scriptWindow);
                                                    String batchCmd = script.get();
                                                    if (batchCmd != null && batchCmd.trim().length() > 0) {
                                                        show.setText("正在执行\n" + batchCmd);
                                                        RFuture rFuture = redissonClient.getScript().evalAsync(RScript.Mode.READ_WRITE, batchCmd, RScript.ReturnType.MAPVALUELIST);
                                                        try {
                                                            show.addLine(objectMapper.writeValueAsString(rFuture.get(10, TimeUnit.SECONDS)));
                                                        } catch (InterruptedException | ExecutionException | JsonProcessingException e) {
                                                            show.addLine(e.getMessage());
                                                        } catch (TimeoutException e) {
                                                            show.addLine(e.getMessage());
                                                        }
                                                    } else {
                                                        show.setText("未输入有效的脚本");
                                                    }
                                                }).withBorder(Borders.singleLine("高级操作"))
                                        )
                                )
                );
            }});
        } finally {
            redissonFactory.close(name);
        }
    }


}
